サーバー上でのfetch On the server, with fetch
from Data Fetching と Cashing キャッシュ と revalidation 再検証
Next.jsでは、サーバー上での各fetch wrapper Next.jsリクエストのCashing キャッシュとrevalidation 再検証の振る舞いを設定できるように、ネイティブのFetch APIを拡張している。
Reactでは、Reactコンポーネントツリーをレンダリング中に自動的にfetch wrapper Next.jsリクエストをメモ化 memorizationする
React Server Components RSCやRoute Handlers Next.jsやServer Actionsで、async/awaitにfetch wrapper Next.jsを利用して、fetchできる。
q. Next.jsのfetch wrapper Next.jsに機能が含まれているようだが、それ以外のFecthパターンではどうするんだろう?
a.Route Segment Next.jsconfigやcache Reactを利用する。
参照 サーバー上でのサードパーティライブラリを使用 On the server, with third-party libraries
code: app/page.tsx
async function getData() {
// 非同期にfetch
const res = await fetch('https://api.example.com/...')
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
if (!res.ok) {
// This will activate the closest error.js Error Boundary
throw new Error('Failed to fetch data')
}
return res.json()
}
export default async function Page() {
const data = await getData()
return <main></main>
}
q. getDataみたいな、API Clientのラッパーは、別ページで定義してimportしたりもするのかな?
a. 問題ない。
Good to know
Next.jsは、React Server Components RSCでData Fetchingするときに必要な機能を提供
例えばCookie クッキーやheaders ヘッダなど
これらはRequest リクエスト web時の情報に依存しているため、Routes ルートをDynamic Rendering 動的レンダリングする原因になる。
? どういうこと?
Static Rendering 静的レンダリングして、キャッシュしたり出来ないということ?
Route Handlers Next.js内のfetch wrapper Next.jsリクエストはメモ化 memorizationされない
なぜなら、Route Handlers Next.jsはReact component treeの一部ではないから
? どういうこと?
Route Handlers Next.jsはReact component treeの一部ではないとは?Route Handlers Next.jsって何?
React component treeだとメモ化 memorizationされるの?
Server Actionsでは、fetch wrapper Next.jsリクエストはCashing キャッシュされない。
デフォルトではcache: no-storeになっている。
? どういうこと?
出来ないわけでなくて、できるんだけど、デフォルトはCashing キャッシュしないようにしていてオススメと言う認識でOK?
Server Actionsって、form タグのaction 属性みたいなもので、POST的だからCashing キャッシュ不要なんでしょう
TypeScriptでReact Server Components RSC内でasync/awaitを使用するには、TypeScript 5.1.3以上と@types/react 18.2.8以上が必要
React Server Components RSCが新しい機能だから、TSのVersionもそれなりに必要という感じかな?
感想
fetchという行動かfetch wrapper Next.jsかFetch APIか分からなくなる時あるね。
このページでいうとみなfetch wrapper Next.js
hr.icon
#### データのキャッシュ
データのCashing キャッシュ
毎回データソースから、再度Data Fetchingしなくてもいいように、データを保存しておくこと
デフォルト
Next.jsはサーバー上のデータCashing キャッシュにfetch wrapper Next.jsの返り値を自動的にCashing キャッシュ
ビルド時やリクエスト時にData Fetchingして、Cashing キャッシュに保存して、各データリクエストで再利用できる
感想
ビルド時にも取得できるんだ。
デフォルトで、Server上のfetch wrapper Next.jsがCashing キャッシュされる事を理解しておきたい。
q
いろんなCashing キャッシュタイミングがあるみたいだけど、どう設定するの?
a. fetch wrapper Next.jsでは、cacheオプションで設定すれば良い。
code:cache.tsx
// 'force-cache' がデフォルト設定なので、省略可能。
fetch('https://...', { cache: 'force-cache' })
// キャッシュされず毎度リクエストされる。
const dynamicData = await fetch(https://..., { cache: 'no-store' })
fetchリクエストがキャッシュされない例外
Server Actions内で使用された場合
q: なんで?
Server Actionsの用途として、Serverで動的に行う処理に使うから?
Route Handlers Next.js内でPOSTメソッドが使用された場合
q: なんで?
POSTメソッドをキャッシュしても再利用することを想定していないから?
hr.icon
#### Revalidating Data
データのrevalidation 再検証
Cashing キャッシュをクリアして最新のデータを再取得するプロセス
用途: 変更された情報、最新情報を取得したい時
revalidation 再検証の2つの方法:
1. Time-based revalidation タイムベースの再検証
一定の時間が経過した後に自動的にデータをrevalidation 再検証
用途: 変更があまり頻繁でなく、新鮮さがそれほど重要でないデータに利用
2. On-demand revalidation オンデマンドの再検証
イベント(例えば、フォームのsubmit)に基づいて手動でデータをrevalidation 再検証
tag-based revalidationまたはpath-based revalidationのアプローチを使用して、一度にデータのグループをrevalidation 再検証できる
用途: できるだけ早く最新のデータを表示したいとき
例: Headless CMS のコンテンツが更新された時
##### Time-based revalidation タイムベースの再検証
方法
fetch wrapper Next.jsのnext.revalidateオプションを使用
リソースのキャッシュ寿命(秒単位)を設定
fetch('https://...', { next: { revalidate: 3600 } })
Route Segment Next.js内のすべてのfetchリクエストを再検証するには、セグメント設定オプションを使用できる
File Conventions: Route Segment Config | Next.js
code: layout.js | page.js
export const revalidate = 3600
Static Rendering 静的レンダリングされたルートで複数のfetch wrapper Next.jsリクエストがあり、それぞれに異なる再検証頻度が設定されている場合
最も低い時間がすべてのリクエストに使用される。
Dynamic Rendering 動的レンダリングされたルートの場合
各fetch wrapper Next.jsリクエストは独立してrevalidation 再検証される
##### On-demand revalidation オンデマンドの再検証
Path(revalidatePath)やキャッシュタグ(revalidateTag)を使って、Server ActionsやRoute Handlers Next.js内でデータを必要に応じてrevalidation 再検証すること
ルートをまたいでfetch wrapper Next.jsリクエストを無効にするためのcache tagging system キャッシュタグシステムがある
1. fetch wrapper Next.jsを使用する際には、一つまたは複数のタグでキャッシュエントリをタグ付けするオプションがある
2.そして、revalidateTagを呼び出して、そのタグに関連付けられたすべてのエントリを再検証できる
例: 以下のfetch wrapper Next.jsリクエストはキャッシュタグcollectionを追加している:
code:app/page.tsx
export default async function Page() {
// キャッシュエントリをタグ付け
// NOTE: 'collection'はkeyみたいな役割で自由に命名できる感じかな?
const res = await fetch('https://...', { next: { tags: 'collection' } })
const data = await res.json()
// ...
}
そして、Server ActionsでrevalidateTagを呼び出して、collectionタグ付けされたこのfetch wrapper Next.jsコールをrevalidation 再検証できる。
code: app/actions.ts
'use server'
import { revalidateTag } from 'next/cache'
export default async function action() {
// そのタグに関連付けられたすべてのエントリを再検証
revalidateTag('collection')
}
感想
利用例イメージは、以下みたいなん?
あるDataリストがあって、あるアイテムを操作しようとする。
そのページでDialogが開かれて、編集できる。
編集完了SubmitにServer Actionsを利用
編集完了後、ページ遷移せずにrevalidation 再検証されたデータを利用してリストが更新される。
例に対する感想
こういうのに時間でrevalidation 再検証するより、更新されたところでrevalidation 再検証するというのは理に叶っている。
q:
tagという概念は何だ?
key的にタグ付けして、Server Actions側で呼び出せるようにしている
revalidatePathの方も知りたいね。
Error Handling Next.jsとデータのrevalidation 再検証
データを再検証しようとしてエラーが発生した場合
最後に成功したデータはCashing キャッシュから引き続き提供される。
次にデータをリクエストする時、Next.jsは再検証を再試行
hr.icon
#### Opting out of Data Caching
データCashing キャッシュからのオプトアウト
fetch wrapper Next.jsリクエストがキャッシュされない場合:
fetch wrapper Next.jsリクエストにcache: 'no-store'が追加された場合
個々のfetch wrapper Next.jsリクエストにrevalidate: 0オプションが追加された場合
Route Handlers Next.js内でPOSTメソッドを使用するfetch wrapper Next.jsリクエストの場合
headers ヘッダやCookie クッキーの使用後のfetch wrapper Next.jsリクエストの場合
? 使用後とは?
const dynamic = 'force-dynamic' Route Segment Next.jsオプションを使用した場合
fetchCacheRoute Segment Next.jsオプションがデフォルトでキャッシュをスキップするように設定された場合
Authorization Header ヘッダやCookie Headerを使用するfetch wrapper Next.jsリクエストで、その上のコンポーネントツリーにキャッシュされていないリクエストがある場合
? むずい。もう少し、丁寧に説明したい。
個々のfetchリクエストでのキャッシュからのオプトアウト
個々のfetchリクエストでキャッシュを避けたい場合は、fetch wrapper Next.jsのcache: 'no-store' に設定
これにより、リクエストごとにデータを動的に取得
複数のfetchリクエストにおけるオプトアウト
Route Segment Next.js(例えば、レイアウトやページ)内に複数のfetchリクエストがある場合セグメント設定オプションを使用してセグメント内のすべてのデータリクエストのキャッシング動作を設定
しかし、個々のfetchリクエストごとにキャッシング動作を設定することをおすすめ
これにより、キャッシング動作をより細かく制御できるため。
感想
難しかったら、一旦、まとめてオプトアウトして、ちょっとずつチューニングでも良いと思うな。